package com.example.sfm.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.sfm.exception.ObjectException;
import com.example.sfm.handler.TemplateStyleHandler;
import com.example.sfm.mapper.ClassesMapper;
import com.example.sfm.mapper.CoursesMapper;
import com.example.sfm.mapper.StudentRecordsMapper;
import com.example.sfm.mapper.UsersMapper;
import com.example.sfm.pojo.dto.StudentRecordsDto;
import com.example.sfm.pojo.dto.StudentRecordsImportDto;
import com.example.sfm.pojo.dto.StudentRecordsQueryDto;
import com.example.sfm.pojo.entity.*;
import com.example.sfm.pojo.vo.StudentRecordsExportVo;
import com.example.sfm.pojo.vo.StudentRecordsVo;
import com.example.sfm.result.PageResult;
import com.example.sfm.service.IGradesService;
import com.example.sfm.service.IStudentRecordsService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author sfm
 * @since 2025-03-07
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class StudentRecordsServiceImpl extends ServiceImpl<StudentRecordsMapper, StudentRecords>
        implements IStudentRecordsService {

    @Lazy
    private final UsersMapper userService;
    private final IGradesService gradesService;
    private final ClassesMapper classesService;
    private final CoursesMapper coursesMapper;

    @Override
    public void createRecord(@NotNull StudentRecordsDto dto) {
        // 校验用户是否存在
        if (!userService.exists(new LambdaQueryWrapper<Users>()
                .eq(Users::getId, dto.getUserId()))) {
            throw new ObjectException("用户不存在");
        }

        if (exists(new LambdaQueryWrapper<StudentRecords>().eq(StudentRecords::getUserId, dto.getUserId()))) {
            throw new ObjectException("用户存在档案");
        }

        StudentRecords entity = BeanUtil.copyProperties(dto, StudentRecords.class)
                .setStatus(0); // 默认学籍状态为在读

        if (!save(entity)) {
            throw new ObjectException("创建档案失败");
        }
    }

    @Override
    public PageResult<?> listRecords(@NotNull StudentRecordsQueryDto dto) {
        LambdaQueryWrapper<StudentRecords> wrapper = new LambdaQueryWrapper<StudentRecords>()
                .like(StringUtils.isNotBlank(dto.getStudentNumber()),
                        StudentRecords::getStudentNumber, dto.getStudentNumber())
                .eq(StringUtils.isNotBlank(dto.getDepartment()),
                        StudentRecords::getDepartment, dto.getDepartment())
                .eq(StringUtils.isNotBlank(dto.getMajor()),
                        StudentRecords::getMajor, dto.getMajor())
                .eq(StringUtils.isNotBlank(dto.getClassName()),
                        StudentRecords::getClassName, dto.getClassName())
                .eq(dto.getStatus() != null,
                        StudentRecords::getStatus, dto.getStatus())
                .orderByDesc(StudentRecords::getAdmissionDate);

        Page<StudentRecords> page = page(new Page<>(dto.getPageNum(), dto.getPageSize()), wrapper);

        return new PageResult<>(page.getTotal(),
                page.getRecords().stream()
                        .map(this::convertToVo)
                        .toList());
    }

    private @NotNull StudentRecordsVo convertToVo(StudentRecords entity) {
        StudentRecordsVo vo = BeanUtil.copyProperties(entity, StudentRecordsVo.class);

        vo.setGenderStr(entity.getGender() == null ? "未知" : convertGender(entity.getGender()))
                .setStudentName(userService.selectById(entity.getUserId()).getFullName())
                .setStatusStr(convertStatus(entity.getStatus()));
        return vo;
    }

    @Contract(pure = true)
    private String convertGender(@NotNull Integer gender) {
        return switch (gender) {
            case 1 -> "男";
            case 2 -> "女";
            default -> "未知";
        };
    }

    @Contract(pure = true)
    private String convertStatus(@NotNull Integer status) {
        return switch (status) {
            case 1 -> "毕业";
            case 2 -> "退学";
            case 3 -> "休学";
            default -> "在读";
        };
    }

    @Override
    @Transactional
    public void updateRecord(Integer id, StudentRecordsDto dto) {
        StudentRecords entity = getById(id);
        if (entity == null) throw new ObjectException("档案不存在");

        // 禁止修改学号
        if (!entity.getStudentNumber().equals(dto.getStudentNumber())) {
            throw new ObjectException("学号不可修改");
        }

        BeanUtil.copyProperties(dto, entity, "studentNumber");
        if (!updateById(entity)) {
            throw new ObjectException("更新失败");
        }
    }

    @Override
    @Transactional
    public void deleteRecords(List<Integer> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            throw new ObjectException("ID列表不能为空");
        }
        if (!removeBatchByIds(ids.stream().distinct().toList())) {
            throw new ObjectException("删除失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importFromExcel(@org.jetbrains.annotations.NotNull MultipartFile file) {
        try {

            List<StudentRecordsImportDto> importData = EasyExcel.read(file.getInputStream())
                    .head(StudentRecordsImportDto.class)
                    .sheet("模板")
                    .doReadSync();

            System.out.println(importData);

            if (importData.isEmpty()) {
                throw new ObjectException("导入数据为空");
            }


            List<StudentRecords> records = importData.stream()
                    .map(this::convertToEntity)
                    .toList();




            Set<Integer> importUserIds = records.stream()
                    .map(StudentRecords::getUserId)
                    .collect(Collectors.toSet());


            List<StudentRecords> existingRecords = baseMapper.selectList(
                    new LambdaQueryWrapper<StudentRecords>()
                            .in(StudentRecords::getUserId, importUserIds)
            );
            Set<Integer> existingUserIds = existingRecords.stream()
                    .map(StudentRecords::getUserId)
                    .collect(Collectors.toSet());


            List<StudentRecords> newRecords = records.stream()
                    .filter(r -> !existingUserIds.contains(r.getUserId()))
                    .collect(Collectors.toList());

            if (newRecords.isEmpty()) {
                throw new ObjectException("所有导入数据均已存在，无法重复添加");
            }


            if (!saveBatch(newRecords, 1000)) {
                throw new ObjectException("批量导入失败");
            }
        } catch (IOException e) {
            throw new ObjectException("文件读取失败：" + e.getMessage() + e);
        }
    }


    private StudentRecords convertToEntity(@NotNull StudentRecordsImportDto dto) {
        System.out.println(dto);
        Users us = userService.selectOne(new LambdaQueryWrapper<Users>()
                .eq(Users::getStudentId, dto.getStudentNumber()));
        if (us == null) {
            throw new ObjectException("用户不存在: " + dto.getStudentNumber());
        }


        // 校验学号唯一性
        if (lambdaQuery().eq(StudentRecords::getStudentNumber, dto.getStudentNumber()).exists()) {
            throw new ObjectException("学号已存在: " + dto.getStudentNumber());
        }

        Classes classes = classesService.selectOne(new LambdaQueryWrapper<Classes>().eq(Classes::getClassName, dto.getClassName()));
        if (classes == null) {
            throw new ObjectException("班级不存在");
        }
        return BeanUtil.copyProperties(dto, StudentRecords.class)
                .setStatus(0) // 默认在读状态
                .setUserId(us.getId())
                .setStudentNumber(us.getStudentId())
                .setClassId(classes.getId())
                .setGpa(BigDecimal.ZERO);
    }

    @Override
    public void exportExcel(HttpServletResponse response, StudentRecordsQueryDto dto) {
        try {
            List<StudentRecordsExportVo> data = listRecords(dto).getItems().stream()
                    .map(obj -> convertToExportVo((StudentRecordsVo) obj))
                    .collect(Collectors.toList());

            String fileName = URLEncoder.encode("学生档案_" + LocalDate.now(), StandardCharsets.UTF_8);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

            EasyExcel.write(response.getOutputStream(), StudentRecordsExportVo.class)
                    .registerWriteHandler(new TemplateStyleHandler())
                    .sheet("学生档案")
                    .doWrite(data);
        } catch (IOException e) {
            throw new ObjectException("导出失败");
        }
    }

    private StudentRecordsExportVo convertToExportVo(StudentRecordsVo vo) {
        return BeanUtil.copyProperties(vo, StudentRecordsExportVo.class);
    }

    @Override
    public void downloadTemplate(HttpServletResponse response) {
        try {
            // 设置响应头
            String fileName = URLEncoder.encode(String.valueOf(LocalDate.now()), StandardCharsets.UTF_8);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");


            EasyExcel.write(response.getOutputStream(), StudentRecordsImportDto.class)
                    .sheet("模板")
                    .doWrite(Collections.emptyList());
        } catch (IOException e) {
            throw new ObjectException("模板下载失败");
        }
    }

    @Override
    @Transactional
    public void changeStatus(Integer id, Integer newStatus) {
        if (!Arrays.asList(0, 1, 2, 3).contains(newStatus)) {
            throw new ObjectException("无效状态值");
        }

        StudentRecords record = getById(id);
        if (record == null) throw new ObjectException("档案不存在");


        if (record.getStatus() == 1 && newStatus == 0) {
            throw new ObjectException("已毕业状态不可变更");
        }

        record.setStatus(newStatus)
                .setUpdatedAt(LocalDateTime.now());

        if (!updateById(record)) {
            throw new ObjectException("状态更新失败");
        }
    }

    @Override
    public StudentRecordsVo finByUserId(Integer id) {
        return convertToVo(getOne(new LambdaQueryWrapper<StudentRecords>()
                .eq(StudentRecords::getUserId, id)));
    }


    @Scheduled(cron = "0 */25 * * * ?")
    public void calculateAllGpa() {
        List<StudentRecords> records = list();
        records.forEach(this::calculateSingleGpa);
    }

    /**
     * 根据单个学生的成绩记录计算加权平均绩点（GPA），并更新学生档案。
     *
     * @param record 单个学生的学籍记录
     */
    public void calculateSingleGpa(@NotNull StudentRecords record) {
        // 根据学生ID查询该学生所有成绩记录
        List<Grades> grades = gradesService.lambdaQuery()
                .eq(Grades::getStudentId, record.getUserId())
                .list();

        // 如果该学生没有任何成绩记录，则将绩点设置为0
        if (grades.isEmpty()) {
            record.setGpa(BigDecimal.ZERO);
        } else {
            // 初始化变量：总加权绩点和（weighted grade points）和总学分
            BigDecimal totalWeightedGradePoints = BigDecimal.ZERO;
            BigDecimal totalCredits = BigDecimal.ZERO;

            // 遍历每一门成绩记录，计算对应课程的加权绩点
            for (Grades grade : grades) {
                // 根据课程ID获取课程信息，目的是获得该课程的学分数
                Courses course = coursesMapper.selectById(grade.getCourseId());
                if (course == null) {
                    // 如果没有查询到课程信息，则跳过该成绩记录
                    continue;
                }
                // 获取课程的学分，若为空则默认学分为1
                Integer credit = course.getCredits();
                if (credit == null) {
                    credit = 1;
                }
                // 将成绩转换为绩点
                BigDecimal gradePoint = convertScoreToGradePoint(grade.getScore());
                // 计算该课程的加权绩点（绩点 * 学分），并累加到总加权绩点中
                totalWeightedGradePoints = totalWeightedGradePoints.add(gradePoint.multiply(BigDecimal.valueOf(credit)));
                // 累加该课程的学分到总学分
                totalCredits = totalCredits.add(BigDecimal.valueOf(credit));
            }

            // 根据总加权绩点和总学分计算加权平均绩点
            // 如果总学分为0则绩点设为0，否则计算总加权绩点除以总学分，保留两位小数
            BigDecimal weightedGpa = totalCredits.compareTo(BigDecimal.ZERO) == 0
                    ? BigDecimal.ZERO
                    : totalWeightedGradePoints.divide(totalCredits, 2, RoundingMode.HALF_UP);
            record.setGpa(weightedGpa);
        }
        // 将计算得到的绩点更新到学生档案中
        updateById(record);
    }


    private BigDecimal convertScoreToGradePoint(@NotNull BigDecimal score) {
        if (score.compareTo(new BigDecimal("90")) >= 0) {
            return new BigDecimal("4.0");
        } else if (score.compareTo(new BigDecimal("80")) >= 0) {
            return new BigDecimal("3.0");
        } else if (score.compareTo(new BigDecimal("70")) >= 0) {
            return new BigDecimal("2.0");
        } else {
            return BigDecimal.ZERO;
        }
    }


}
